﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SharpShare.Activation;

namespace SharpShare.Storage.FileSystem {
    public class FileStorageContainer : FileStorageItem, IStorageContainer, IStorageProvider {
        private object _contentsLock = new object();
        private FolderLookup _contents;
        private EventHandler _contentsChangedHandler;
        private FileSystemWatcher _fsWatcher;

        public FileStorageContainer(DirectoryInfo info, IFileStorageItemFactory factory)
            : base(info, factory) {

        }

        public new DirectoryInfo Info { get { return (DirectoryInfo)((FileStorageItem)this).Info; } }

        protected override void Move(string newPath) {
            this.Info.MoveTo(newPath);
        }

        #region IStorageContainer Members

        public IStorageLookup Contents {
            get {
                this.DemandExists();

                lock (_contentsLock) {
                    if (_contents == null || !PathUtils.IsEqual(_contents.Path, this.Info.FullName)) {
                        _contents = new FolderLookup(this.Info.FullName, this.StorageItemFactory);
                    }

                    return _contents;
                }
            }
        }

        public IStorageFile CreateFile(string name) {
            try {
                string path = PathUtils.Combine(this.Info.FullName, PathUtils.EscapeFilename(name));
                FileInfo info = new FileInfo(path);

                if (info.Exists) throw new StorageItemAlreadyExistsException();

                info.Create().Close();

                return this.Contents[name].DemandFile();
            } catch (StorageException) {
                throw;
            } catch (Exception ex) {
                throw HandleException(ex, ExceptionAttemptedAction.CreateFile);
            }
        }

        public IStorageContainer CreateContainer(string name) {
            try {
                string path = PathUtils.Combine(this.Info.FullName, PathUtils.EscapeFilename(name));
                DirectoryInfo info = new DirectoryInfo(path);

                if (info.Exists) throw new StorageItemAlreadyExistsException();

                info.Create();

                return this.Contents[name].DemandContainer();
            } catch (StorageException) {
                throw;
            } catch (Exception ex) {
                throw HandleException(ex, ExceptionAttemptedAction.CreateContainer);
            }
        }

        #endregion

        #region ICastable<IStorageProvider> Members

        IStorageProvider ICastable<IStorageProvider>.TryCast() {
            if (this.StorageItemFactory.Options.RootPath == null) {
                return this;
            }

            if (this.IsRoot) {
                return this;
            }

            return null;
        }

        #endregion

        #region IStorageProvider Members

        public event EventHandler ContentsChanged {
            add {
                _contentsChangedHandler += value;

                if (_fsWatcher == null) {
                    _fsWatcher = new FileSystemWatcher(this.Info.FullName);
                    _fsWatcher.NotifyFilter = (NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size);
                    _fsWatcher.IncludeSubdirectories = true;
                    _fsWatcher.Created += new FileSystemEventHandler(FileSystemWatcherCreated);
                    _fsWatcher.Changed += new FileSystemEventHandler(FileSystemWatcherChanged);
                    _fsWatcher.Deleted += new FileSystemEventHandler(FileSystemWatcherDeleted);
                    _fsWatcher.Error += new ErrorEventHandler(FileSystemWatcherError);
                    _fsWatcher.Renamed += new RenamedEventHandler(FileSystemWatcherRenamed);
                }

                _fsWatcher.EnableRaisingEvents = true;
            }
            remove {
                _contentsChangedHandler -= value;

                if (_contentsChangedHandler == null) {
                    _fsWatcher.Dispose();
                    _fsWatcher = null;
                }
            }
        }

        ulong IStorageProvider.AvailableBytes {
            get { return (ulong)this.DriveInfo.AvailableFreeSpace; }
        }

        ulong IStorageProvider.TotalBytes {
            get { return (ulong)this.DriveInfo.TotalSize; }
        }

        Searching.IStorageSearchEngine IStorageProvider.SearchEngine {
            get { return null; }
        }

        #endregion

        protected virtual void OnContentsChanged() {
            if (_contentsChangedHandler != null) {
                try {
                    _contentsChangedHandler(this, EventArgs.Empty);
                } catch { }
            }
        }

        #region FileSystemWatcher Event Handlers

        void FileSystemWatcherRenamed(object sender, RenamedEventArgs e) {
            this.OnContentsChanged();
        }

        void FileSystemWatcherError(object sender, ErrorEventArgs e) {
            this.OnContentsChanged();
        }

        void FileSystemWatcherDeleted(object sender, FileSystemEventArgs e) {
            this.OnContentsChanged();
        }

        void FileSystemWatcherChanged(object sender, FileSystemEventArgs e) {
            this.OnContentsChanged();
        }

        void FileSystemWatcherCreated(object sender, FileSystemEventArgs e) {
            this.OnContentsChanged();
        }

        #endregion

    }
}
